import Vue from 'vue'
/**
 * Deep copy the given object considering circular structure.
 * This function caches all nested objects and its copies.
 * If it detects circular structure, use cached copy to avoid infinite loop.
 *
 * @param {*} obj
 * @param {Array<Object>} cache
 * @return {*}
 */
export function deepCopy(obj, cache = []) {
  // just return if obj is immutable value
  if (obj === null || typeof obj !== 'object') {
    return obj
  }

  // if obj is hit, it is in circular structure
  const hit = find(cache, c => c.original === obj)
  if (hit) {
    return hit.copy
  }

  const copy = Array.isArray(obj) ? [] : {}
  // put the copy into cache at first
  // because we want to refer it in recursive deepCopy
  cache.push({
    original: obj,
    copy
  })

  Object.keys(obj).forEach(key => {
    copy[key] = deepCopy(obj[key], cache)
  })

  return copy
}
/**
 * 判断两个数组是否有交集
 * @param {array} arr1 
 * @param {array} arr2 
 */
export function uniqueArr(arr1, arr2) {
  for (const item of arr1) {
    if (arr2.indexOf(item) > -1) return true
  }
  return false
}

/**
 * 函数防抖 (只执行最后一次点击)
 * @param fn
 * @param delay
 * @returns {Function}
 * @author: zhanghan
 */
export const Debounce = (fn, t) => {
  let delay = t || 200
  let timer
  // let num = 0
  return function () {
    // num++
    // console.log(num)
    let args = arguments
    if (timer) {
      clearTimeout(timer)
    }
    timer = setTimeout(() => {
      timer = null
      fn.apply(this, args)
    }, delay)
  }
}
/**
* 函数节流
* @param fn
* @param interval
* @returns {Function}
* @author: zhanghan
*/
export const Throttle = (fn, t) => {
  let last
  let timer
  let interval = t || 500
  return function () {
    let args = arguments
    let now = +new Date()
    if (last && now - last < interval) {
      clearTimeout(timer)
      timer = setTimeout(() => {
        last = now
        fn.apply(this, args)
      }, interval)
    } else {
      last = now
      fn.apply(this, args)
    }
  }
}
// 判断两个简单对象是否相等
export const isObjectValueEqual = (a, b) => {
  var aProps = Object.getOwnPropertyNames(a);
  var bProps = Object.getOwnPropertyNames(b);

  if (aProps.length != bProps.length) {
    return false;
  }

  for (var i = 0; i < aProps.length; i++) {
    var propName = aProps[i];

    if (a[propName] !== b[propName]) {
      return false;
    }
  }

  return true;
}

var toString = Object.prototype.toString;

function isFunction(obj) {
  return toString.call(obj) === '[object Function]'
}
// 判断相等
export const eq = (a, b, aStack, bStack) => {

  // === 结果为 true 的区别出 +0 和 -0
  if (a === b) return a !== 0 || 1 / a === 1 / b;

  // typeof null 的结果为 object ，这里做判断，是为了让有 null 的情况尽早退出函数
  if (a == null || b == null) return false;

  // 判断 NaN
  if (a !== a) return b !== b;

  // 判断参数 a 类型，如果是基本类型，在这里可以直接返回 false
  var type = typeof a;
  if (type !== 'function' && type !== 'object' && typeof b != 'object') return false;

  // 更复杂的对象使用 deepEq 函数进行深度比较
  return deepEq(a, b, aStack, bStack);
};

function deepEq(a, b, aStack, bStack) {

  // a 和 b 的内部属性 [[class]] 相同时 返回 true
  var className = toString.call(a);
  if (className !== toString.call(b)) return false;

  switch (className) {
    case '[object RegExp]':
    case '[object String]':
      return '' + a === '' + b;
    case '[object Number]':
      if (+a !== +a) return +b !== +b;
      return +a === 0 ? 1 / +a === 1 / b : +a === +b;
    case '[object Date]':
    case '[object Boolean]':
      return +a === +b;
  }

  var areArrays = className === '[object Array]';
  // 不是数组
  if (!areArrays) {
    // 过滤掉两个函数的情况
    if (typeof a != 'object' || typeof b != 'object') return false;

    var aCtor = a.constructor,
      bCtor = b.constructor;
    // aCtor 和 bCtor 必须都存在并且都不是 Object 构造函数的情况下，aCtor 不等于 bCtor， 那这两个对象就真的不相等啦
    if (aCtor !== bCtor && !(isFunction(aCtor) && aCtor instanceof aCtor && isFunction(bCtor) && bCtor instanceof bCtor) && ('constructor' in a && 'constructor' in b)) {
      return false;
    }
  }


  aStack = aStack || [];
  bStack = bStack || [];
  var length = aStack.length;

  // 检查是否有循环引用的部分
  while (length--) {
    if (aStack[length] === a) {
      return bStack[length] === b;
    }
  }

  aStack.push(a);
  bStack.push(b);

  // 数组判断
  if (areArrays) {

    length = a.length;
    if (length !== b.length) return false;

    while (length--) {
      if (!eq(a[length], b[length], aStack, bStack)) return false;
    }
  }
  // 对象判断
  else {

    var keys = Object.keys(a),
      key;
    length = keys.length;

    if (Object.keys(b).length !== length) return false;
    while (length--) {

      key = keys[length];
      if (!(b.hasOwnProperty(key) && eq(a[key], b[key], aStack, bStack))) return false;
    }
  }

  aStack.pop();
  bStack.pop();
  return true;

}

/**
 * 解析属性参数
 * @param {string} params 属性参数
 * @return {Object} [ {k:'',v:''}, ]
 */
export const parseParams = (params) => {
  try {
    let list = JSON.parse(params) || []
    let canChooseAttr = list.filter((e) => { return e.children.length }) // 有值的属性
    return canChooseAttr.map((i) => {
      // let values = (i.children).map((j)=>{return j.key})
      return {
        k: i.key,
        v: (i.children).map((j) => { return j.key })
      }
    })
  } catch (error) {
    console.log(error)
    return { k: '', v: '' }
  }

}

/**
 * 解析搜索返回属性参数
 * @param {string} params 属性参数
 * @return {Object} [ {k:'',v:''}, ]
 */
export const parseParamList = (params) => {
  try {
    let list = [];
    let paramList = JSON.parse(params) || []
    paramList.forEach(v => {
      list = [...list, ...v]
    })
    let canChooseAttr = list.filter((e) => { return e.children && e.children.length }) // 有值的属性
    // let canChooseAttr = list
    return canChooseAttr.map((i) => {
      // let values = (i.children).map((j)=>{return j.key})
      return {
        k: i.key,
        v: (i.children).map((j) => { return j.key })
      }
    })
  } catch (error) {
    console.log(error)
    return { k: '', v: '' }
  }

}
/**
 * 倒计时 剩余时间 毫秒
 * @param {number} remain 剩余时间 毫秒
 * @param {object} obj {day,hour,minute,second}
 * @param {function} fn 结束后执行的方法
 */
export const countDowm = (remain, obj, fn) => {
  let remainder = remain
  let timer = setInterval(() => {
    remainder = remainder - 1000
    let timediff = remainder / 1000
    if (timediff < 1) {
      clearInterval(timer)
      obj.day = 0
      //获取还剩多少小时
      obj.hour = 0
      //获取还剩多少分钟
      obj.minute = 0
      //获取还剩多少秒
      obj.second = 0
      if (typeof fn == 'function') fn()
    } else {
      obj.day = parseInt(timediff / 3600 / 24)
      //获取还剩多少小时
      obj.hour = parseInt((timediff / 3600) % 24)
      //获取还剩多少分钟
      obj.minute = parseInt((timediff / 60) % 60)
      //获取还剩多少秒
      obj.second = parseInt(timediff % 60)
    }
  }, 1000)
}

/**
 * 倒计时 剩余时间 毫秒
 * @param {number} remain 剩余时间 毫秒
 * @param {object} leftTimeObj {leftTime}
 * @param {function} fn 结束后执行的方法
 * @Date 2019/09/18
 */
export const countDownPro = (remain, leftTimeObj, fn) => {
  let remainder = remain
  let ld = leftTimeObj.day = 0, lh = leftTimeObj.hour = 0, lh2 = leftTimeObj.hour = 0,
    lm = leftTimeObj.minute = 0, ls = leftTimeObj.second = 0
  function fmtTime() {
    remainder = remainder - 1000
    let timediff = remainder / 1000
    if (timediff < 1) {
      clearInterval(timer)
      ld = 0, lh = 0, lm = 0, ls = 0
      if (typeof fn === 'function') fn()
    } else {
      ld = parseInt(timediff / 3600 / 24)
      // 获取还剩多少小时
      lh = parseInt((timediff / 3600) % 24)
      lh2 = parseInt(timediff / 3600)
      // 获取还剩多少分钟
      lm = parseInt((timediff / 60) % 60)
      // lm2 = parseInt(timediff / 60)
      // 获取还剩多少秒
      ls = parseInt(timediff % 60)
      // ls2 = parseInt(timediff % 60)
      // 组装数据
      let hourFmt = '', minuteFmt = '', secondFmt = ''
      if (lh2 < 10) { hourFmt = '0' + lh2 } else hourFmt = lh2
      if (lm < 10) { minuteFmt = '0' + lm } else minuteFmt = lm
      if (ls < 10) { secondFmt = '0' + ls } else secondFmt = ls
      leftTimeObj.leftTime = hourFmt + ':' + minuteFmt + ':' + secondFmt
      leftTimeObj.day = ld
      leftTimeObj.hour = lh
      leftTimeObj.minute = lm
      leftTimeObj.second = ls
    }
  }
  fmtTime() // 立即执行一次
  let timer = setInterval(fmtTime, 1000)
}

/**
 * 获取字符串的字符大小
 * @param {string} message 字符串
 */
export const getStrLenght = (message) => {
  let strlenght = 0; //初始定义长度为0
  let txtval = message.trim();
  for (let i = 0; i < txtval.length; i++) {
    if (isCN(txtval.charAt(i)) == true) {
      strlenght = strlenght + 2; //中文为2个字符
    } else {
      strlenght = strlenght + 1; //英文一个字符
    }
  }
  return strlenght
}
export function isCN(str) { //判断是不是中文
  let regexCh = /[u00-uff]/;
  return !regexCh.test(str);
}
/**
 * 通过价格区间获取商品单价
 * @param {array} priceRanges 价格区间
 * @param {number} num 数量
 */
export function getSkuPrice(priceRanges, num) {
  let price = 0
  let length = priceRanges.length
  if (num >= priceRanges[length - 1][0]) {
    price = priceRanges[length - 1][2]
  } else {
    for (let index = 0; index < length; index++) {
      const priceRange = priceRanges[index]
      const priceRangeNext = priceRanges[index + 1]
      if (num >= priceRange[0] && num < priceRangeNext[0]) price = priceRange[2]
    }
  }
  return price
}

/**
 * 计算字符长度 字母及各种字符1 中文2
 * @param {String} str 待计算字符
 * @Date 2019/12/11
 */
export function gblen(str) {
  var len = 0
  for (var i = 0; i < str.length; i++) {
    if (str.charCodeAt(i) > 127 || str.charCodeAt(i) == 94) {
      len += 2
    } else {
      len++
    }
  }
  return len
}

export function deepStr(obj) {
  return JSON.parse(JSON.stringify(obj))
}



const hasOwnProperty = Object.prototype.hasOwnProperty;

export function noop() { };

export function hasOwn(obj, key) {
  return hasOwnProperty.call(obj, key);
};

function extend(to, _from) {
  for (let key in _from) {
    to[key] = _from[key];
  }
  return to;
};

export function toObject(arr) {
  var res = {};
  for (let i = 0; i < arr.length; i++) {
    if (arr[i]) {
      extend(res, arr[i]);
    }
  }
  return res;
};

export const getValueByPath = function (object, prop) {
  prop = prop || '';
  const paths = prop.split('.');
  let current = object;
  let result = null;
  for (let i = 0, j = paths.length; i < j; i++) {
    const path = paths[i];
    if (!current) break;

    if (i === j - 1) {
      result = current[path];
      break;
    }
    current = current[path];
  }
  return result;
};

export function getPropByPath(obj, path, strict) {
  let tempObj = obj;
  path = path.replace(/\[(\w+)\]/g, '.$1');
  path = path.replace(/^\./, '');

  let keyArr = path.split('.');
  let i = 0;
  for (let len = keyArr.length; i < len - 1; ++i) {
    if (!tempObj && !strict) break;
    let key = keyArr[i];
    if (key in tempObj) {
      tempObj = tempObj[key];
    } else {
      if (strict) {
        throw new Error('please transfer a valid prop path to form item!');
      }
      break;
    }
  }
  return {
    o: tempObj,
    k: keyArr[i],
    v: tempObj ? tempObj[keyArr[i]] : null
  };
};

export const generateId = function () {
  return Math.floor(Math.random() * 10000);
};

export const valueEquals = (a, b) => {
  // see: https://stackoverflow.com/questions/3115982/how-to-check-if-two-arrays-are-equal-with-javascript
  if (a === b) return true;
  if (!(a instanceof Array)) return false;
  if (!(b instanceof Array)) return false;
  if (a.length !== b.length) return false;
  for (let i = 0; i !== a.length; ++i) {
    if (a[i] !== b[i]) return false;
  }
  return true;
};

export const escapeRegexpString = (value = '') => String(value).replace(/[|\\{}()[\]^$+*?.]/g, '\\$&');

// TODO: use native Array.find, Array.findIndex when IE support is dropped
export const arrayFindIndex = function (arr, pred) {
  for (let i = 0; i !== arr.length; ++i) {
    if (pred(arr[i])) {
      return i;
    }
  }
  return -1;
};

export const arrayFind = function (arr, pred) {
  const idx = arrayFindIndex(arr, pred);
  return idx !== -1 ? arr[idx] : undefined;
};

// coerce truthy value to array
export const coerceTruthyValueToArray = function (val) {
  if (Array.isArray(val)) {
    return val;
  } else if (val) {
    return [val];
  } else {
    return [];
  }
};

export const isIE = function () {
  return !Vue.prototype.$isServer && !isNaN(Number(document.documentMode));
};

export const isEdge = function () {
  return !Vue.prototype.$isServer && navigator.userAgent.indexOf('Edge') > -1;
};

export const autoprefixer = function (style) {
  if (typeof style !== 'object') return style;
  const rules = ['transform', 'transition', 'animation'];
  const prefixes = ['ms-', 'webkit-'];
  rules.forEach(rule => {
    const value = style[rule];
    if (rule && value) {
      prefixes.forEach(prefix => {
        style[prefix + rule] = value;
      });
    }
  });
  return style;
};

export const kebabCase = function (str) {
  const hyphenateRE = /([^-])([A-Z])/g;
  return str
    .replace(hyphenateRE, '$1-$2')
    .replace(hyphenateRE, '$1-$2')
    .toLowerCase();
};

//字符串转base64
export const encode = function (str) {
  // 对字符串进行编码
  var encode = encodeURI(str);
  // 对编码的字符串转化base64
  var base64 = btoa(encode);
  return base64;
}

// base64转字符串
export const decode = function (base64) {
  // 对base64转编码
  var decode = atob(base64);
  // 编码转字符串
  var str = decodeURI(decode);
  return str;
}



/**
 * 删除arr1中包含arr2值的数组 
 * arr1=[1,2,3] arr2=[2] removeDataFromOtherArr(arr1,arr2) => [1,3]
 * @param {Array} arr1 原数组
 * @param {Array} arr2 需要删除的数组
 */
export function removeDataFromOtherArr(arr1, arr2) {
  for (let i = 0; i < arr2.length; i++) {
    for (let j = 0; j < arr1.length; j++) {
      if (arr2[i] == arr1[j]) {
        let indexs = arr1.indexOf(arr1[j]);
        arr1.splice(indexs, 1);
      }
    }
  }
  return arr1
}

/**
 * 删除数组里的一个元素（第一个相同的元素）
 * @param {Array} arr 原数组
 * @param {Any} e 需要删除的元素
 */
export function removeEleFromArr(arr, e) {
  let index = arr.indexOf(e)
  if (index > -1) return arr.splice(index, 1)
}


/**
 * js判断一个数组是否包含另一个数组
 * @param {Array} aa 大数组
 * @param {Any} bb 判断数组
 */
export function isContained(aa, bb) {
  if (!(aa instanceof Array) || !(bb instanceof Array) || ((aa.length < bb.length))) {
    return false;
  }
  for (var i = 0; i < bb.length; i++) {
    var flag = false;
    for (var j = 0; j < aa.length; j++) {
      if (aa[j] == bb[i]) {
        flag = true;
        break;
      }
    }
    if (flag == false) {
      return flag;
    }
  }

  return true;
}

/**
 * 密码安全等级计算
 */
export const pwdSafe = {
  num: 0, lower: 0, upper: 0, other: 0,
  len: function () {
    return this.num + this.lower + this.upper + this.other
  },
  getScore: function () {
    let sum = 0
    if (this.len() >= 8) {
      sum += 25
    } else if (this.len() > 4) {
      sum += 10
    } else if (this.len() > 0) {
      sum += 5
    }
    if (this.lower > 0 && this.upper > 0) {
      sum += 20
    } else if (this.lower > 0 || this.upper > 0) {
      sum + 10
    }
    if (this.num > 1) {
      sum += 20
    } else if (this.num > 0) {
      sum += 10
    }
    if (this.other > 1) {
      sum += 25
    } else if (this.other > 0) {
      sum += 10
    }
    if (this.num > 0 && this.lower > 0 && this.upper > 0 && this.other > 0) {
      sum += 5
    } else if (this.num > 0 && this.other > 0 && (this.lower > 0 || this.upper > 0)) {
      sum += 3
    } else if (this.num > 0 && (this.lower > 0 || this.upper > 0)) {
      sum += 2
    }
    return sum
  },
  getLevel: function () {
    let s = this.getScore()
    if (s >= 80) {
      return 'high'
    } else if (s > 30) {
      return 'mid'
    }
    return 'low'
  },
  initPass: function (val) {
    for (let i = 0; i < val.length; i++) {
      let c = val.charCodeAt(i);
      if (c >= 48 && c <= 57) {
        pwdSafe.num++;
      } else if (c >= 97 && c <= 122) {
        pwdSafe.lower++;
      } else if (c >= 65 && c <= 90) {
        pwdSafe.upper++;
      } else {
        pwdSafe.other++;
      }
    }
  }
};
export function rafThrottle(fn) {
  let locked = false;
  return function (...args) {
    if (locked) return;
    locked = true;
    window.requestAnimationFrame(_ => {
      fn.apply(this, args);
      locked = false;
    });
  };
}

/**
 * 判断是否为图片
 * @param {string} fileName 文件名
 */
export const isImage = (fileName) => {
  let imgType = ['.jpg', '.jpeg', '.gif', '.png', '.bmp']
  return imgType.filter(v => fileName.indexOf(v) > -1)
}

/**
 * 检查最终价格是否有效
 * 返回true为有效，false为无效
 * 有效条件为大于0，小于10亿(9999999)
 * @param {Number} price 价格
 */
export const checkLimitPrice = (price) => {
  return price >= 0 && price <= 9999999
}

/**
 * 检查是否为空值
 * @param {any} val 
 */
export const isNull = (val) => {
  return (val === null || val === undefined || val === '' || val === NaN)
}